/**
 *  JFTSTool - Java Full Text Search tool.
 *  Copyright (C) <2009>  <grupo JFTS>
 *  This program is free software: you can redistribute it and/or modify
 *	it under the terms of the GNU General Public License as published by
 *	the Free Software Foundation, either version 3 of the License, or
 *	(at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *  
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.jftstool.semantic;

import java.io.File;
import java.sql.SQLException;

import org.jftstool.config.DBConnectionConfig;
import org.jftstool.config.JFTSConfiguration;
import org.jftstool.config.exceptions.JFTSConfigException;
import org.jftstool.semantic.exceptions.JFTSSemanticException;

import com.hp.hpl.jena.db.DBConnection;
import com.hp.hpl.jena.db.IDBConnection;
import com.hp.hpl.jena.ontology.Individual;
import com.hp.hpl.jena.ontology.OntClass;
import com.hp.hpl.jena.ontology.OntDocumentManager;
import com.hp.hpl.jena.ontology.OntModel;
import com.hp.hpl.jena.ontology.OntModelSpec;
import com.hp.hpl.jena.rdf.model.InfModel;
import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.rdf.model.ModelFactory;
import com.hp.hpl.jena.rdf.model.ModelMaker;
import com.hp.hpl.jena.reasoner.Reasoner;
import com.hp.hpl.jena.reasoner.ReasonerRegistry;
import com.hp.hpl.jena.reasoner.ValidityReport;

/**
 * This class represents the semantic model persistent BD.
 * @author grupo JFTS
 *
 */
public class DBSemanticModel implements ISemanticModel {
	private OntModel base = null;
	private OntModel inf = null;
	private JFTSConfiguration config = null;
	private String ontNS = null;
	private DBConnectionConfig conn = null;	
	
	
	public DBSemanticModel(JFTSConfiguration config) throws JFTSSemanticException {
		try {
			
			this.config = config;			
			ontNS = config.getSemanticConfig().getNameSpace();
			conn = config.getSemanticConfig().getDefaultDBConn();
			
			if (conn == null){
				throw new JFTSSemanticException("Error, can't find default DBModelConnection");				
			}
		}
		catch (JFTSConfigException e) {
			throw new JFTSSemanticException("Error, to load ontNS from config file");			
		}
	}
	
	
	public DBSemanticModel(JFTSConfiguration config, String dbConn) throws JFTSSemanticException {
		try {
			
			this.config = config;			
			ontNS = config.getSemanticConfig().getNameSpace();
			conn = config.getSemanticConfig().getModelDBConn(dbConn);
			
			if (dbConn == null){
				throw new JFTSSemanticException("Error, can't find DBModelConnection: " + dbConn);				
			}
		}
		catch (JFTSConfigException e) {
			throw new JFTSSemanticException("Error, to load ontNS from config file");			
		}
	}


	@Override
	public void createSemanitcModel() throws JFTSSemanticException {
		try {
			ModelMaker maker = _getRDBMaker(conn.getArg("driver"), conn.getArg("url"), 
											conn.getArg("username"), conn.getArg("password"),
											conn.getArg("type"), true);
			_loadOntology(maker, ontNS, config.getSemanticConfig().getOntologyFile());
			_createBaseModel(maker, ontNS);
		}
		catch (JFTSConfigException e) {
			throw new JFTSSemanticException(e.getMessage());
		}			
	}


	@Override
	public void loadSemanicModel() throws JFTSSemanticException {
		ModelMaker maker = _getRDBMaker(conn.getArg("driver"), conn.getArg("url"), 
										conn.getArg("username"), conn.getArg("password"),
										conn.getArg("type"), false);
		_createBaseModel(maker, ontNS);
	}
	

	@Override
	public void addNewInstance(String className, String instanceName, boolean replace) throws JFTSSemanticException {
		OntClass aux = base.getOntClass(ontNS + className);
		if (aux != null){
			if (replace){
				removeInstance(instanceName);
			}
			aux.createIndividual(ontNS + instanceName);			
		}
		else{
			throw new JFTSSemanticException("Error, ontClass: " + className + " not exists");
		}
	}
	
	
	@Override
	public void removeInstance(String instanceName) throws JFTSSemanticException {
		Individual ind = base.getIndividual(ontNS + instanceName);
		if (ind != null){
			ind.remove();
		}		
	}
	
	
	private ModelMaker _getRDBMaker(String dbDriver, String dbURL, String dbUser, String dbPw,
			String dbType, boolean cleanDB) throws JFTSSemanticException {
	
		//Load the JDBC driver class
        try {
        	Class.forName(dbDriver);
        	
    		//Create database connection
            IDBConnection conn  = new DBConnection(dbURL, dbUser, dbPw, dbType);
            
            // do we need to clean the database?
            if (cleanDB) {
                conn.cleanDB();
            }
            
            //Create a model maker object
            return ModelFactory.createModelRDBMaker(conn);
        	
		}
        catch (ClassNotFoundException e) {
        	e.printStackTrace();
			throw new JFTSSemanticException("Error, can't load JDBC dirver: " + dbDriver);
		}
        catch (SQLException e) {
			e.printStackTrace();
			throw new JFTSSemanticException("Error, can't delete dataBase");			
		}
	}

	
	private void _loadOntology(ModelMaker maker, String name, String owlFile)
			throws JFTSSemanticException {

		String owlURI = new File(owlFile).toURI().toString();
		ValidityReport report = _validateOntologyFile(owlURI);
		if (report.isValid()){
			 OntDocumentManager.getInstance().addAltEntry( name, owlURI);		
			 Model base = maker.createModel( name, false );
	
		        // now we plug that base model into an ontology model that also uses
		        // the given model maker to create storage for imported models
		        OntModel m = ModelFactory.createOntologyModel( OntModelSpec.OWL_DL_MEM , base );
	
		        // now load the source document, which will also load any imports
		        m.read(name, "RDF/XML");
		}
		else{
			throw new JFTSSemanticException("Error to validate ontology file");
		}
	}
	
	private void _createBaseModel(ModelMaker maker, String modelName) {
		 Model aux = maker.createModel( modelName, false );
		 base = ModelFactory.createOntologyModel( OntModelSpec.OWL_DL_MEM , aux );
	}
	
	public void createInfModel(ModelMaker maker, String modelName) {
		Model aux = maker.createModel( modelName, false );
		inf = ModelFactory.createOntologyModel( OntModelSpec.OWL_MEM_MICRO_RULE_INF , aux );
	}
	
	public OntModel getBaseModel(){
		return base;
	}
	
	public OntModel getInfModel(){
		return inf;
	}
	
	public String getOntNS(){
		return ontNS;
	}
	
	private ValidityReport _validateOntologyFile(String owlURI) throws JFTSSemanticException {
		ValidityReport report = null;
		Model emptyModel = ModelFactory.createDefaultModel();
		Reasoner reasoner = ReasonerRegistry.getOWLReasoner();
		InfModel model = ModelFactory.createInfModel(reasoner, emptyModel);

		model.read(owlURI, "RDF/XML");
		report = model.validate();

		return report;
	}


	@Override
	public String getOntClassPath(String className, OntModel model)
			throws JFTSSemanticException {
			
		OntClass aux = model.getOntClass(ontNS + className);		
		if (aux == null){
			return "";
		}
		else{
			if (aux.getSuperClass() != null){
				return getOntClassPath(aux.getSuperClass().getLocalName(), model) + "/" + className;
			}
			else{
				return className;
			}
			
		}
	}
}